Skip to content

UT拦截统计 - #13

Merged
JinnanDuan merged 5 commits into
openBitFun:masterfrom
azi44eo:master
May 19, 2026
Merged

UT拦截统计#13
JinnanDuan merged 5 commits into
openBitFun:masterfrom
azi44eo:master

Conversation

@azi44eo

@azi44eo azi44eo commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added "UT Gate History" page accessible via new menu item to view historical UT test runs.
    • New /api/v1/ut-gate-runs endpoints for reporting and retrieving UT gate test results.
    • Page supports filtering by time range, interception status, merge request URL, and job name.
    • Pagination and sorting capabilities for browsing large result sets.
  • Documentation

    • Added comprehensive specifications for UT gate reporting and history page implementation.

Review Change Stack

weixin_53033691 and others added 5 commits May 12, 2026 10:24
utf8mb4 下 VARCHAR(1024) 全列与 created_at 联合索引超过 3072 字节上限。
改为 mr_url(191) 前缀索引,ORM mysql_length 与 spec §5.3 同步。

Co-authored-by: Cursor <cursoragent@cursor.com>
修复 TS2345:onChange 为 [Dayjs|null, Dayjs|null]|null,state 不可写死为 [Dayjs,Dayjs]。

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements a complete UT gate run reporting feature enabling Jenkins integration to persist test gate results and a frontend history page for browsing and filtering those results. It includes database schema, ORM models, REST APIs with idempotency and integration token authentication, comprehensive request validation, and a React-based UI with filtering and pagination.

Changes

UT Gate Run Reporting and History

Layer / File(s) Summary
Database schema and ORM model
database/V1.1.2__create_ut_gate_run.sql, backend/models/ut_gate_run.py, backend/models/__init__.py, backend/services/schema_check_service.py
ut_gate_run table added with idempotency key unique constraint and composite indexes for time, interception, and job tracking; SQLAlchemy ORM model maps all fields including timestamps, Jenkins context (base URL, job name, build number, build URL, MR URL), and test result flags (is_intercepted, ut_exit_code).
API schemas and request validation
backend/schemas/ut_gate_run.py, backend/tests/test_ut_gate_run_query.py
Three Pydantic schemas define request body (UtGateRunCreate), response item (UtGateRunItem), and query parameters (UtGateRunQuery) with validators for string trimming, URL normalization, build number bounds, mutual exclusivity between MR URL filters, and time range consistency; tests validate constraints for date format mixing, start/end ordering, and sort field restrictions.
Integration token authentication and configuration
.env.example, backend/core/config.py, backend/core/dependencies.py
Configuration field UT_GATE_INTEGRATION_TOKEN defaults to empty string; verify_ut_gate_integration_token dependency enforces Bearer header and validates token using constant-time HMAC comparison, returning 401 when missing or mismatched.
Service layer with idempotency and query logic
backend/services/ut_gate_run_service.py, backend/tests/test_ut_gate_run_service.py
Service implements idempotent create (pre-check by idempotency_key, return 200 on matching payload match, raise 409 on conflict, insert and return 201 on new record), and paginated list with optional AND-based filtering for reported-at range, interception flag, MR URL exact/substring match, job name substring, with dynamic sorting and total count via subquery.
Backend API endpoints and routing
backend/api/v1/ut_gate_run.py, backend/api/router.py, backend/tests/test_openapi.py
GET /api/v1/ut-gate-runs paginates records via UtGateRunQuery and returns PageResponse[UtGateRunItem]; POST endpoint creates records with integration token verification, logs idempotency conflicts, returns 409 or 201/200 based on create_ut_gate_run result; router registered under /api/v1 and OpenAPI test verifies both operations appear.
Frontend API service and types
frontend/src/services/utGate.ts, frontend/src/services/index.ts
UtGateRunItem and UtGateRunListParams TypeScript interfaces mirror backend contracts; toSearchParams helper converts optional params to query string (excluding undefined/null/empty values), applies pagination defaults; utGateApi.list performs GET to /ut-gate-runs and returns PageResponse[UtGateRunItem].
Frontend history page, routing, and navigation
frontend/src/pages/ut-gate-history/UtGateHistoryPage.tsx, frontend/src/routes/index.tsx, frontend/src/layouts/MainLayout.tsx
UtGateHistoryPage fetches and displays records with RangePicker for date filtering, Select for interception flag, Inputs for MR/job name filters; table columns include formatted timestamps, interception status tags, MR/build hyperlinks (target=_blank), nullable exit code, and idempotency key tooltip; reset clears filters and page resets to 1; route registered at /ut-gate-history with menu item "UT门禁历史" in sidebar.
Specifications and architecture documentation
spec/15_ut_gate_jenkins_report_spec.md, spec/16_ut_gate_report_post_api_spec.md, spec/17_ut_gate_runs_get_api_spec.md, spec/18_ut_gate_history_frontend_spec.md, docs/05_technical_architecture.md
Comprehensive specs define requirements (collection, persistence, display via root.sh and Jenkins integration), POST API with Bearer token, idempotency semantics (201/200/409), and request fields; GET API specifies query parameter validation, filtering, sorting (default reported_at DESC), and pagination; frontend spec defines route, menu, table layout, and service contract; architecture docs updated to reference database table, API routes, and UI page.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit hops through the gate,
Recording when tests celebrate,
Each idempotent key aligned,
A history of results, neatly designed,
From Jenkins reports to frontend's sight,
The UT door swings open bright! 🚪✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'UT拦截统计' (UT Interception Statistics) is misleading; the PR primarily implements a complete UT gate run reporting and history system with API endpoints, database schema, frontend page, and authentication—not just statistics functionality. Rename the title to reflect the main changes, such as 'Implement UT Gate Run Reporting and History API' or 'Add UT Gate Run Management System (API + Frontend + DB)'.
Docstring Coverage ⚠️ Warning Docstring coverage is 15.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
spec/16_ut_gate_report_post_api_spec.md (1)

69-70: ⚡ Quick win

Make unknown-field handling deterministic.

这里同时允许“忽略未知键”或“422”两种行为,会导致实现与联调口径漂移。建议在本文档内固定一种(推荐 extra="ignore"),并与 OpenAPI/测试保持一致。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/16_ut_gate_report_post_api_spec.md` around lines 69 - 70, The spec
currently permits two behaviors for unknown keys in ut_gate_run (ignore or
return 422), causing nondeterministic implementations; fix by standardizing on
Pydantic's extra="ignore" behavior: update the ut_gate_run schema handling to
ignore unknown fields such as error_message, git_remote_url, git_commit_sha,
mr_id, summary_line, and ensure the OpenAPI document and tests (including any
validation in the POST/PUT handler that constructs or validates ut_gate_run)
explicitly reflect extra="ignore" so runtime, API docs, and tests are
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/05_technical_architecture.md`:
- Line 227: The doc currently contradicts itself about Jenkins interaction;
update the technical architecture text so the data flow is consistently "Jenkins
→ Backend API → MySQL": modify the ut_gate_run row (the line referencing POST
/api/v1/ut-gate-runs and UT_GATE_INTEGRATION_TOKEN) to explicitly state Jenkins
posts to Backend API which authenticates using UT_GATE_INTEGRATION_TOKEN and the
Backend writes to MySQL, and remove or revise the earlier sentence that claims
"Jenkins 无直接交互、直接写 MySQL" so it reflects the single path; ensure references to
spec/16_ut_gate_report_post_api_spec.md and the endpoint POST
/api/v1/ut-gate-runs remain and clearly document the security boundary at the
Backend API.

In `@frontend/src/pages/ut-gate-history/UtGateHistoryPage.tsx`:
- Around line 106-114: The reset handler onReset calls fetchList(1, 20) before
the state setters (setDateRange, setInterceptFilter, setMrUrlExact,
setMrUrlContains, setJobNameContains, setPageSize) have taken effect, so the
request uses stale filters; instead invoke fetchList with the explicit
reset/default parameters (e.g. page=1, size=20, dateRange=null,
interceptFilter="all", mrUrlExact="", mrUrlContains="", jobNameContains="") or
adjust fetchList to accept an overrides object and pass those defaults from
onReset so the network call uses the intended cleared filters rather than
relying on pending state updates.

In `@spec/18_ut_gate_history_frontend_spec.md`:
- Around line 129-130: The spec must be updated to require that large ids are
transmitted as JSON strings or parsed with a big-number aware parser before
JSON.parse so precision isn't lost; change any guidance around using BigInt or
String(row.id) to explicitly state that BigInt conversion only works if the
backend sends ids as strings (e.g., "id":"12345678901234567890") or the client
uses a specialized parser like json-bigint to preserve integers >
Number.MAX_SAFE_INTEGER prior to any JSON.parse, and add a note that converting
a numeric value to BigInt after a standard JSON.parse cannot recover lost
precision.

---

Nitpick comments:
In `@spec/16_ut_gate_report_post_api_spec.md`:
- Around line 69-70: The spec currently permits two behaviors for unknown keys
in ut_gate_run (ignore or return 422), causing nondeterministic implementations;
fix by standardizing on Pydantic's extra="ignore" behavior: update the
ut_gate_run schema handling to ignore unknown fields such as error_message,
git_remote_url, git_commit_sha, mr_id, summary_line, and ensure the OpenAPI
document and tests (including any validation in the POST/PUT handler that
constructs or validates ut_gate_run) explicitly reflect extra="ignore" so
runtime, API docs, and tests are consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8446cc35-63c9-4224-b9e8-e8cfc5c5acf5

📥 Commits

Reviewing files that changed from the base of the PR and between 4c26d93 and d72fa8a.

📒 Files selected for processing (24)
  • .env.example
  • backend/api/router.py
  • backend/api/v1/ut_gate_run.py
  • backend/core/config.py
  • backend/core/dependencies.py
  • backend/models/__init__.py
  • backend/models/ut_gate_run.py
  • backend/schemas/ut_gate_run.py
  • backend/services/schema_check_service.py
  • backend/services/ut_gate_run_service.py
  • backend/tests/test_openapi.py
  • backend/tests/test_ut_gate_run_query.py
  • backend/tests/test_ut_gate_run_service.py
  • database/V1.1.2__create_ut_gate_run.sql
  • docs/05_technical_architecture.md
  • frontend/src/layouts/MainLayout.tsx
  • frontend/src/pages/ut-gate-history/UtGateHistoryPage.tsx
  • frontend/src/routes/index.tsx
  • frontend/src/services/index.ts
  • frontend/src/services/utGate.ts
  • spec/15_ut_gate_jenkins_report_spec.md
  • spec/16_ut_gate_report_post_api_spec.md
  • spec/17_ut_gate_runs_get_api_spec.md
  • spec/18_ut_gate_history_frontend_spec.md

| case_offline_type | 全表 | 全字段 CRUD | 管理员操作 |
| sys_audit_log | 全表 | INSERT only | 系统自动写入 |
| report_snapshot | 全表 | INSERT / SELECT | 管理员生成报告时写入 |
| ut_gate_run | 全表 | INSERT(幂等) | Jenkins 经 `POST /api/v1/ut-gate-runs` 写入,鉴权为 `UT_GATE_INTEGRATION_TOKEN`(见 `spec/16_ut_gate_report_post_api_spec.md`) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

统一 Jenkins 数据流描述,避免架构口径冲突。

这里写的是 Jenkins 通过 POST /api/v1/ut-gate-runs 写入;但文档前文仍有“Jenkins 无直接交互、直接写 MySQL”的描述。建议统一为单一路径:Jenkins → Backend API → MySQL,避免实现和安全边界被误读。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/05_technical_architecture.md` at line 227, The doc currently contradicts
itself about Jenkins interaction; update the technical architecture text so the
data flow is consistently "Jenkins → Backend API → MySQL": modify the
ut_gate_run row (the line referencing POST /api/v1/ut-gate-runs and
UT_GATE_INTEGRATION_TOKEN) to explicitly state Jenkins posts to Backend API
which authenticates using UT_GATE_INTEGRATION_TOKEN and the Backend writes to
MySQL, and remove or revise the earlier sentence that claims "Jenkins 无直接交互、直接写
MySQL" so it reflects the single path; ensure references to
spec/16_ut_gate_report_post_api_spec.md and the endpoint POST
/api/v1/ut-gate-runs remain and clearly document the security boundary at the
Backend API.

Comment on lines +106 to +114
const onReset = () => {
setDateRange(null);
setInterceptFilter("all");
setMrUrlExact("");
setMrUrlContains("");
setJobNameContains("");
setPageSize(20);
void fetchList(1, 20);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset currently queries with pre-reset filters

Line 113 calls fetchList(1, 20) before Lines 107-112 state updates take effect, so the request can still carry old filter values from the callback closure.

Suggested fix
-  const fetchList = useCallback(async (nextPage: number, nextPageSize: number) => {
-    if (mrUrlExact.trim() && mrUrlContains.trim()) {
+  type FetchOverrides = Partial<{
+    dateRange: UtGateDateRange;
+    interceptFilter: "all" | "yes" | "no";
+    mrUrlExact: string;
+    mrUrlContains: string;
+    jobNameContains: string;
+  }>;
+
+  const fetchList = useCallback(async (nextPage: number, nextPageSize: number, overrides?: FetchOverrides) => {
+    const exact = (overrides?.mrUrlExact ?? mrUrlExact).trim();
+    const contains = (overrides?.mrUrlContains ?? mrUrlContains).trim();
+    const jobContains = (overrides?.jobNameContains ?? jobNameContains).trim();
+    const appliedDateRange = overrides?.dateRange ?? dateRange;
+    const appliedIntercept = overrides?.interceptFilter ?? interceptFilter;
+
+    if (exact && contains) {
       message.warning("MR 精确与 MR 子串互斥,请只填其一");
       return;
     }
@@
-      if (dateRange?.[0]) params.start_time = dateRange[0].format("YYYY-MM-DD");
-      if (dateRange?.[1]) params.end_time = dateRange[1].format("YYYY-MM-DD");
-      if (interceptFilter === "yes") params.is_intercepted = true;
-      if (interceptFilter === "no") params.is_intercepted = false;
-      const m = mrUrlExact.trim();
-      const mc = mrUrlContains.trim();
-      if (m) params.mr_url = m;
-      if (mc) params.mr_url_contains = mc;
-      const j = jobNameContains.trim();
-      if (j) params.job_name_contains = j;
+      if (appliedDateRange?.[0]) params.start_time = appliedDateRange[0].format("YYYY-MM-DD");
+      if (appliedDateRange?.[1]) params.end_time = appliedDateRange[1].format("YYYY-MM-DD");
+      if (appliedIntercept === "yes") params.is_intercepted = true;
+      if (appliedIntercept === "no") params.is_intercepted = false;
+      if (exact) params.mr_url = exact;
+      if (contains) params.mr_url_contains = contains;
+      if (jobContains) params.job_name_contains = jobContains;
@@
-  const onReset = () => {
+  const onReset = () => {
     setDateRange(null);
     setInterceptFilter("all");
     setMrUrlExact("");
     setMrUrlContains("");
     setJobNameContains("");
+    setPage(1);
     setPageSize(20);
-    void fetchList(1, 20);
+    void fetchList(1, 20, {
+      dateRange: null,
+      interceptFilter: "all",
+      mrUrlExact: "",
+      mrUrlContains: "",
+      jobNameContains: "",
+    });
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ut-gate-history/UtGateHistoryPage.tsx` around lines 106 -
114, The reset handler onReset calls fetchList(1, 20) before the state setters
(setDateRange, setInterceptFilter, setMrUrlExact, setMrUrlContains,
setJobNameContains, setPageSize) have taken effect, so the request uses stale
filters; instead invoke fetchList with the explicit reset/default parameters
(e.g. page=1, size=20, dateRange=null, interceptFilter="all", mrUrlExact="",
mrUrlContains="", jobNameContains="") or adjust fetchList to accept an overrides
object and pass those defaults from onReset so the network call uses the
intended cleared filters rather than relying on pending state updates.

Comment on lines +129 to +130
- **`id`**:`BIGINT` 可能超过 **`Number.MAX_SAFE_INTEGER`**。表格展示推荐 **`String(row.id)`** 或使用 **`BigInt`** 再转字符串;**避免**对大 `id` 做依赖精度的数值运算。
- **日期时间**:后端返回 ISO 字符串;展示层可用 **`dayjs`**(若项目已用)或 **`toLocaleString`** 格式化,**不**强制与时区策略改动后端。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In JavaScript, if JSON contains an integer larger than Number.MAX_SAFE_INTEGER, can converting the parsed Number to BigInt recover the original exact integer?

💡 Result:

No, converting a parsed Number to BigInt cannot recover the original exact integer if the number exceeded Number.MAX_SAFE_INTEGER [1][2]. When JSON.parse encounters a number, it parses it into a JavaScript Number type, which follows the IEEE 754 double-precision floating-point format [3][4]. This format has a limited mantissa (53 bits), meaning it can only represent integers exactly within the range of -(2^53 - 1) to 2^53 - 1 (Number.MAX_SAFE_INTEGER) [1]. If a JSON number exceeds this range, the precision is lost during the initial parsing process [5][2]. The value is rounded to the nearest representable double-precision float [4]. Because this rounding occurs before you can access the value or convert it to a BigInt, the original exact integer information is already discarded [6][2]. To handle integers larger than Number.MAX_SAFE_INTEGER without losing precision, you must prevent the JSON parser from converting them into standard Numbers [2]. Common strategies include: 1. Using a specialized library (e.g., json-bigint) that parses large numbers as strings or BigInts directly [7][2]. 2. Pre-processing the JSON string to wrap large numbers in quotes (converting them to strings) before passing them to JSON.parse [7][4][8]. 3. Using the reviver parameter of JSON.parse in conjunction with the raw source text (if the environment provides access to it via context) to manually parse the original string representation as a BigInt [9][6]. Note that the standard reviver function receives the already-parsed (and thus already corrupted) Number, so it cannot recover the lost precision on its own [6][2].

Citations:


Clarify how large id values are transmitted and parsed to prevent precision loss.

The spec suggests using BigInt to handle id values exceeding Number.MAX_SAFE_INTEGER, but this only works if the backend transmits large ids as JSON strings, not as JSON numbers. If the backend sends large ids as JSON numbers, precision is already lost at the JSON.parse stage—converting to BigInt afterwards cannot recover it (IEEE 754 double-precision can only represent integers exactly up to 2^53−1).

The spec should explicitly require: either the backend must send large ids as strings in the JSON response, or use a specialized JSON parser (e.g., json-bigint) that handles large numbers before precision loss occurs. Relying on post-parse BigInt conversion is insufficient.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/18_ut_gate_history_frontend_spec.md` around lines 129 - 130, The spec
must be updated to require that large ids are transmitted as JSON strings or
parsed with a big-number aware parser before JSON.parse so precision isn't lost;
change any guidance around using BigInt or String(row.id) to explicitly state
that BigInt conversion only works if the backend sends ids as strings (e.g.,
"id":"12345678901234567890") or the client uses a specialized parser like
json-bigint to preserve integers > Number.MAX_SAFE_INTEGER prior to any
JSON.parse, and add a note that converting a numeric value to BigInt after a
standard JSON.parse cannot recover lost precision.

@JinnanDuan
JinnanDuan merged commit a770258 into openBitFun:master May 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants